/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "Wed_framework_config.h"
#include "Wed_gpio.h"

/*******************************************************************************
 * Static
 ******************************************************************************/
static struct Wed_list_head __g_gpio_list;
static struct Wed_mutex     __g_gpio_list_lock;

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief GPIO 设备链表初始化
 *
 * @return 成功返回 0
 */
int Wed_gpio_list_init(void){
    Wed_list_head_init(&__g_gpio_list);

    return Wed_mutex_init(&__g_gpio_list_lock);
}

/**
 * \brief 设备添加函数
 */
static int __dev_add(struct Wed_gpio *p_gpio_dev){
    int ret = 0;

    ret = Wed_mutex_lock(&__g_gpio_list_lock, 5000);
    if (ret != 0) {
        return ret;
    }

    Wed_list_node_add_tail(&p_gpio_dev->node, &__g_gpio_list);

    Wed_mutex_unlock(&__g_gpio_list_lock);

    return ret;
}

/**
 * \brief GPIO 设备写函数
 */
static int __dev_init(void *p_dev){
    struct Wed_gpio *p_gpio_dev = (struct Wed_gpio *)p_dev;

    if (p_gpio_dev->p_opts->p_fn_init) {
        return p_gpio_dev->p_opts->p_fn_init(p_gpio_dev->p_opts_arg,
                                             p_gpio_dev->port_num,
                                             p_gpio_dev->pin_num,
                                             p_gpio_dev->flag);
    }

    return 0;
}

/**
 * \brief UART 设备写函数
 */
static int __dev_write(void      *p_dev,
                        uint8_t  *p_data,
                        uint32_t  data_len){
    if ((data_len != 1) || ((p_data[0] != 0) && (p_data[0] != 1))) {
        return -WED_EINVAL;
    }

    return Wed_gpio_sta_set((struct Wed_gpio *)p_dev, p_data[0]);
}

/* \brief 设备操作函数集 */
static struct Wed_dev_opts __g_gpio_opts = {
        .p_fn_init  = __dev_init,
        .p_fn_write = __dev_write,
};

/**
 * \brief GPIO 设备注册函数
 *
 * @param p_gpio_name GPIO 名字
 * @param p_opts      GPIO 设备操作函数集
 * @param p_opts_arg  操作函数参数
 * @param port_num    GPIO 端口号
 * @param pin_num     GPIO 引脚号
 * @param flag        标志
 *
 * @return 成功返回 0
 */
int Wed_gpio_register(char                 *p_gpio_name,
                      struct Wed_gpio_opts *p_opts,
                      void                 *p_opts_arg,
                      uint32_t              port_num,
                      uint32_t              pin_num,
                      uint32_t              flag){
    int              ret;
    struct Wed_gpio *p_gpio_dev = NULL;

    if ((p_gpio_name == NULL) ||
            (strlen(p_gpio_name) >= WED_DEV_NAME_LEN) ||
            (p_opts == NULL)) {
        return -WED_EINVAL;
    }

    /* 检查设备是否已存在 */
    if (Wed_dev_find_by_name(p_gpio_name)) {
        return -WED_EEXIST;
    }

#if WED_MEM_DYMANICS
    p_gpio_dev = Wed_calloc(1, sizeof(struct Wed_gpio));
    if (p_gpio_dev == NULL) {
        return -WED_ENOMEM;
    }

    p_gpio_dev->p_opts     = p_opts;
    p_gpio_dev->p_opts_arg = p_opts_arg;
    p_gpio_dev->port_num   = port_num;
    p_gpio_dev->pin_num    = pin_num;
    p_gpio_dev->flag       = flag;

    ret = Wed_mutex_init(&p_gpio_dev->lock);
    if (ret != 0) {
        goto __failed;
    }

    ret = Wed_dev_init_add(&p_gpio_dev->dev,
                            p_gpio_name,
                            WED_DEV_TYPE_GPIO,
                         &__g_gpio_opts,
                            p_gpio_dev);
    if (ret == 0) {
        __dev_add(p_gpio_dev);

        return 0;
    }

__failed:
    Wed_free(p_gpio_dev);

    return ret;
#else

#endif
}

/**
 * \brief 初始化所有 GPIO
 *
 * @return 成功返回 0
 */
int Wed_gpio_init_all(void){
    int                   ret    = WED_NO_DEV;
    int                   result = 0;
    struct Wed_list_node *p_node = NULL;
    struct Wed_gpio      *p_dev  = NULL;

    result = Wed_mutex_lock(&__g_gpio_list_lock, 5000);
    if (result != 0) {
        return result;
    }

    Wed_list_for_each_node(p_node, &__g_gpio_list){
        p_dev = WED_CONTAINER_OF(p_node, struct Wed_gpio, node);

        ret = __dev_init(p_dev);
        if (ret != 0) {
            WED_WARN_PRINT("gpio \"%s\" init failed(%d)", p_dev->dev.name);
        }

        result |= ret;
    }

    if ((result == 0) && (ret != WED_NO_DEV)) {
        WED_INFO_PRINT("gpio init success");
    }

    Wed_mutex_unlock(&__g_gpio_list_lock);

    return result;
}

/**
 * \brief 根据名字获取 GPIO 设备结构体
 *
 * @param p_gpio_name GPIO 名字
 *
 * @return 找到返回 GPIO 设备结构体，没找到返回 NULL
 */
struct Wed_gpio *Wed_gpio_find_by_name(char *p_gpio_name){
    int                   ret;
    struct Wed_list_node *p_node = NULL;
    struct Wed_gpio      *p_dev  = NULL;

    ret = Wed_mutex_lock(&__g_gpio_list_lock, 5000);
    if (ret != 0) {
        return NULL;
    }

    Wed_list_for_each_node(p_node, &__g_gpio_list){
        p_dev = WED_CONTAINER_OF(p_node, struct Wed_gpio, node);
        if (strcmp(p_dev->dev.name, p_gpio_name) == 0) {
            Wed_mutex_unlock(&__g_gpio_list_lock);

            return p_dev;
        }
    }

    Wed_mutex_unlock(&__g_gpio_list_lock);

    return NULL;
}

/**
 * \brief GPIO 状态设置
 *
 * @param p_gpio GPIO 设备
 * @param sta    GPIO 状态
 *
 * @return 成功返回 0
 */
int Wed_gpio_sta_set(struct Wed_gpio *p_gpio, uint8_t sta){
    int ret;

    if (p_gpio == NULL) {
        return -WED_EINVAL;
    }
    if (p_gpio->p_opts->p_fn_sta_set == NULL) {
        return -WED_ENOSUPPORT;
    }
    ret = Wed_mutex_lock(&p_gpio->lock, 5000);
    if (ret != 0) {
        return ret;
    }

    ret = p_gpio->p_opts->p_fn_sta_set(p_gpio->p_opts_arg, p_gpio->port_num, p_gpio->pin_num, sta);

    Wed_mutex_unlock(&p_gpio->lock);

    return ret;
}

/**
 * \brief GPIO 翻转
 *
 * @param p_gpio GPIO 设备
 *
 * @return 成功返回 0
 */
int Wed_gpio_toggle(struct Wed_gpio *p_gpio){
    int ret;

    if (p_gpio == NULL) {
        return -WED_EINVAL;
    }
    if (p_gpio->p_opts->p_fn_sta_toggle == NULL) {
        return -WED_ENOSUPPORT;
    }
    ret = Wed_mutex_lock(&p_gpio->lock, 5000);
    if (ret != 0) {
        return ret;
    }

    ret = p_gpio->p_opts->p_fn_sta_toggle(p_gpio->p_opts_arg, p_gpio->port_num, p_gpio->pin_num);

    Wed_mutex_unlock(&p_gpio->lock);

    return ret;
}
